package raytracer.objects;

import raytracer.math.Vector3d;
import raytracer.misc.Ray;
import raytracer.misc.AppWideConstants;

/**
 * 
 * @author Thorsten Roth
 * @version 20.10.2006
 */
public class Triangle extends ThreeDObject {

	private Vector3d[] vertices;
	private Vector3d normal;
	
	public Triangle(Vector3d p1,Vector3d p2,Vector3d p3) {
		super();
		vertices=new Vector3d[]{p1,p2,p3};
		calcNormal();
	}
	
	private void calcNormal() {
		normal=new Vector3d();
		Vector3d edge1=new Vector3d(vertices[1]);
		Vector3d edge2=new Vector3d(vertices[2]);
		edge1.sub(vertices[0]);
		edge2.sub(vertices[1]);
		normal.x=edge1.y*edge2.z-edge1.z*edge2.y;
		normal.y=edge1.z*edge2.x-edge1.x*edge2.z;
		normal.z=edge1.x*edge2.y-edge1.y*edge2.x;
		normal.normalize();
	}
	
	public Vector3d getPoint(int i) {
		if (i>0 && i<3)
			return vertices[i];
		else
			return null;
	}
	
	public Double intersect(Ray ray,Vector3d intersectionPoint,Vector3d intersectionNormal) {
		/*if (ray.getOrigin().dot(normal)<0)
			return null;*/ // Culling :S
		
		double t;
		double det,inv_det;
		double u,v;
		Vector3d tvec;
		Vector3d direction=new Vector3d(ray.getDirection());
		Vector3d origin=new Vector3d(ray.getOrigin());
		
		/* find vectors for two edges sharing vertices[0] */
		Vector3d edge1=new Vector3d(vertices[1]);
		Vector3d edge2=new Vector3d(vertices[2]);
		edge1.sub(vertices[0]);
		edge2.sub(vertices[0]);
		
		/* begin calculating determinant - also used to calculate U-parameter */
		Vector3d pvec=direction.cross(edge2);
		
		/* determinant near zero -> ray lies in plane of triangle */
		det=edge1.dot(pvec);
		
		if (det<AppWideConstants.EPSILON && det>-AppWideConstants.EPSILON)
			return null;
		
		inv_det=1.0/det;
		
		/* calculate distance from vertices[0] to ray origin */
		tvec=new Vector3d(origin);
		tvec.sub(vertices[0]);
		
		/* calculate U parameter and test bounds */
		u=tvec.dot(pvec)*inv_det;
		if (u<0 || u>1)
			return null;
		
		/* prepare to test V parameter*/
		Vector3d qvec=tvec.cross(edge1);
		
		/* calculate V parameter and test bounds */
		v=direction.dot(qvec)*inv_det;
		if (v<0 || u+v>1)
			return null;
		
		/* calculate t, scale parameters, ray intersects triangle */
		t=edge2.dot(qvec)*inv_det;
			
		if (intersectionNormal!=null) {
			intersectionNormal.set(normal);
		}
		
		if (intersectionPoint!=null) {
			intersectionPoint.x=origin.x+t*direction.x;
			intersectionPoint.y=origin.y+t*direction.y;
			intersectionPoint.z=origin.z+t*direction.z;
		}
		
		return t;
		
	}

	@Override
	public double getMaxX() {
		return Math.max(
				vertices[0].x,
				Math.max(vertices[1].x,vertices[2].x)
		);
	}

	@Override
	public double getMaxY() {
		return Math.max(
				vertices[0].y,
				Math.max(vertices[1].y,vertices[2].y)
		);
	}

	@Override
	public double getMaxZ() {
		return Math.max(
				vertices[0].z,
				Math.max(vertices[1].z,vertices[2].z)
		);
	}

	@Override
	public double getMinX() {
		return Math.min(
				vertices[0].x,
				Math.min(vertices[1].x,vertices[2].x)
		);
	}

	@Override
	public double getMinY() {
		return Math.min(
				vertices[0].y,
				Math.min(vertices[1].y,vertices[2].y)
		);
	}

	@Override
	public double getMinZ() {
		return Math.min(
				vertices[0].z,
				Math.min(vertices[1].z,vertices[2].z)
		);
	}

	@Override
	public boolean isInfinite() {
		return false;
	}
}